• 问题

    先来看一个错误的例子:

    public class CollectionClassifier {
        public static String classify(Set<?> s) {
            return "Set";
        }
    
        public static String classify(List<?> lst) {
            return "List";
        }
    
        public static String classify(Collection<?> c) {
            return "Unknown Collection";
        }
    
        public static void main(String[] args) {
            Collection<?>[] collections = {
                new HashSet<String>(),
                new ArrayList<BigInteger>(),
                new HashMap<String, String>().values()
            };
    
            for (Collection<?> c : collections)
                System.out.println(classify(c));
        }
    }
    

    我们希望打出的是set,list,Unknown Collection。实际上,它的输出是Unknown Collection。这是因为classify方法被重载了,实际上调用哪个重载方法,是在编译时就已经决定了。在这个例子中编译器都认为是Collection<?>类,所以输出的是三个Unknown Collection。因此,在使用重载是应该注意哪些问题?

  • 解决

    1. 调用哪个具体的重载方法是在编译时就决定了,根据方法中参数的编译时类型。而对于被覆盖的方法的选择则是动态的,是根据调用该方法的对象的运行时类型,来选择合适的“被覆盖的版本”。覆盖是用来实现多态的,而重载并不是;

    2. 使用重载,安全而保守的策略是:永远不要写两个具有相同参数数目的重载方法

    3. 如果一定要重载,那么对于一对重载方法,至少要有一个对应的参数在两个重载方法中的类型“完全不同”。可以看下面这个例子:

      public class SetList {
          public static void main(String[] args) {
              Set<Integer> set = new TreeSet<Integer>();
              List<Integer> list = new ArrayList<Integer>();
      
              for (int i = -3; i < 3; i++) {
                  set.add(i);
                  list.add(i);
              }
      
              for (int i = 0; i < 3; i++) {
                  set.remove(i);
                  list.remove(i);
              }
      
              System.out.println(set + " " + list);
          }
      }
      //输出结果
      [-3, -2, -1] [-2, 0, 2]
      

      Set的输出结果如同我们想的一样,但是List的结果不一样。实际发生的情况是:set.remove(E),选择重载方法的参数实际上是Integer,这里进行了自动装箱,把int装箱成了Integer;对于List,有两个重载函数,这里直接重载了list.remove(i),并没有重载到list.remove(E),是从list的指定位置进行remove,得到结果为-2,0,2。这里最根本的原因在于,由于泛型和自动拆箱和装箱,使得remove(E)和remove(i)这两个方法中的参类型上数并没有“根本的不同”。

  • 结论

    能够重载并不意味者应该重载,一般来说,对于多个具有相同参数数目的重载方法,还是尽量避免使用重载。如果不能避免重载,就需要保证每一个重载方法的参数类型无论经过怎样的转换(如泛型和自动拆箱和装箱)后都能“完全不同”,从而根据参数类型能够指向不同的重载方法。

results matching ""

    No results matching ""